// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "ui/aura/test/aura_test_base.h"
#include "ui/aura/test/test_windows.h"
#include "ui/aura/window.h"
#include "ui/aura/window_event_dispatcher.h"
#include "ui/compositor/layer.h"
#include "ui/compositor/test/test_layers.h"
#include "ui/views/view.h"
#include "ui/views/view_constants_aura.h"
#include "ui/views/widget/widget.h"

namespace views {
namespace {

    // Creates a control widget with the passed in parameters.
    // The caller takes ownership of the returned widget.
    Widget* CreateControlWidget(aura::Window* parent, const gfx::Rect& bounds)
    {
        Widget::InitParams params(Widget::InitParams::TYPE_CONTROL);
        params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
        params.parent = parent;
        params.bounds = bounds;
        Widget* widget = new Widget();
        widget->Init(params);
        return widget;
    }

    // Sets the name of |window| and |window|'s layer to |name|.
    void SetWindowAndLayerName(aura::Window* window, const std::string& name)
    {
        window->SetName(name);
        window->layer()->set_name(name);
    }

    // Returns a string containing the name of each of the child windows (bottommost
    // first) of |parent|. The format of the string is "name1 name2 name3 ...".
    std::string ChildWindowNamesAsString(const aura::Window& parent)
    {
        std::string names;
        typedef std::vector<aura::Window*> Windows;
        for (Windows::const_iterator it = parent.children().begin();
             it != parent.children().end(); ++it) {
            if (!names.empty())
                names += " ";
            names += (*it)->name();
        }
        return names;
    }

    typedef aura::test::AuraTestBase WindowReordererTest;

    // Test that views with layers and views with associated windows are reordered
    // according to the view hierarchy.
    TEST_F(WindowReordererTest, Basic)
    {
        scoped_ptr<Widget> parent(CreateControlWidget(root_window(),
            gfx::Rect(0, 0, 100, 100)));
        parent->Show();
        aura::Window* parent_window = parent->GetNativeWindow();

        View* contents_view = new View();
        parent->SetContentsView(contents_view);

        // 1) Test that layers for views and layers for windows associated to a host
        // view are stacked below the layers for any windows not associated to a host
        // view.
        View* v = new View();
        v->SetPaintToLayer(true);
        v->layer()->set_name("v");
        contents_view->AddChildView(v);

        scoped_ptr<Widget> w1(CreateControlWidget(parent_window,
            gfx::Rect(0, 1, 100, 101)));
        SetWindowAndLayerName(w1->GetNativeView(), "w1");
        w1->Show();
        scoped_ptr<Widget> w2(CreateControlWidget(parent_window,
            gfx::Rect(0, 2, 100, 102)));
        SetWindowAndLayerName(w2->GetNativeView(), "w2");
        w2->Show();

        EXPECT_EQ("w1 w2", ChildWindowNamesAsString(*parent_window));
        EXPECT_EQ("v w1 w2",
            ui::test::ChildLayerNamesAsString(*parent_window->layer()));

        View* host_view2 = new View();
        contents_view->AddChildView(host_view2);
        w2->GetNativeView()->SetProperty(kHostViewKey, host_view2);
        EXPECT_EQ("w2 w1", ChildWindowNamesAsString(*parent_window));
        EXPECT_EQ("v w2 w1",
            ui::test::ChildLayerNamesAsString(*parent_window->layer()));

        View* host_view1 = new View();
        w1->GetNativeView()->SetProperty(kHostViewKey, host_view1);
        contents_view->AddChildViewAt(host_view1, 0);
        EXPECT_EQ("w1 w2", ChildWindowNamesAsString(*parent_window));
        EXPECT_EQ("w1 v w2",
            ui::test::ChildLayerNamesAsString(*parent_window->layer()));

        // 2) Test the z-order of the windows and layers as a result of reordering the
        // views.
        contents_view->ReorderChildView(host_view1, -1);
        EXPECT_EQ("w2 w1", ChildWindowNamesAsString(*parent_window));
        EXPECT_EQ("v w2 w1",
            ui::test::ChildLayerNamesAsString(*parent_window->layer()));

        contents_view->ReorderChildView(host_view2, -1);
        EXPECT_EQ("w1 w2", ChildWindowNamesAsString(*parent_window));
        EXPECT_EQ("v w1 w2",
            ui::test::ChildLayerNamesAsString(*parent_window->layer()));

        // 3) Test the z-order of the windows and layers as a result of reordering the
        // views in situations where the window order remains unchanged.
        contents_view->ReorderChildView(v, -1);
        EXPECT_EQ("w1 w2", ChildWindowNamesAsString(*parent_window));
        EXPECT_EQ("w1 w2 v",
            ui::test::ChildLayerNamesAsString(*parent_window->layer()));

        contents_view->ReorderChildView(host_view2, -1);
        EXPECT_EQ("w1 w2", ChildWindowNamesAsString(*parent_window));
        EXPECT_EQ("w1 v w2",
            ui::test::ChildLayerNamesAsString(*parent_window->layer()));

        // Work around for bug in NativeWidgetAura.
        // TODO: fix bug and remove this.
        parent->Close();
    }

    // Test that different orderings of:
    // - adding a window to a parent widget
    // - adding a "host" view to a parent widget
    // - associating the "host" view and window
    // all correctly reorder the child windows and layers.
    TEST_F(WindowReordererTest, Association)
    {
        scoped_ptr<Widget> parent(CreateControlWidget(root_window(),
            gfx::Rect(0, 0, 100, 100)));
        parent->Show();
        aura::Window* parent_window = parent->GetNativeWindow();

        View* contents_view = new View();
        parent->SetContentsView(contents_view);

        aura::Window* w1 = aura::test::CreateTestWindowWithId(0,
            parent->GetNativeWindow());
        SetWindowAndLayerName(w1, "w1");

        aura::Window* w2 = aura::test::CreateTestWindowWithId(0, NULL);
        SetWindowAndLayerName(w2, "w2");

        View* host_view2 = new View();

        // 1) Test that parenting the window to the parent widget last results in a
        //    correct ordering of child windows and layers.
        contents_view->AddChildView(host_view2);
        w2->SetProperty(views::kHostViewKey, host_view2);
        EXPECT_EQ("w1", ChildWindowNamesAsString(*parent_window));
        EXPECT_EQ("w1",
            ui::test::ChildLayerNamesAsString(*parent_window->layer()));

        parent_window->AddChild(w2);
        EXPECT_EQ("w2 w1", ChildWindowNamesAsString(*parent_window));
        EXPECT_EQ("w2 w1",
            ui::test::ChildLayerNamesAsString(*parent_window->layer()));

        // 2) Test that associating the window and "host" view last results in a
        // correct ordering of child windows and layers.
        View* host_view1 = new View();
        contents_view->AddChildViewAt(host_view1, 0);
        EXPECT_EQ("w2 w1", ChildWindowNamesAsString(*parent_window));
        EXPECT_EQ("w2 w1",
            ui::test::ChildLayerNamesAsString(*parent_window->layer()));

        w1->SetProperty(views::kHostViewKey, host_view1);
        EXPECT_EQ("w1 w2", ChildWindowNamesAsString(*parent_window));
        EXPECT_EQ("w1 w2",
            ui::test::ChildLayerNamesAsString(*parent_window->layer()));

        // 3) Test that parenting the "host" view to the parent widget last results
        // in a correct ordering of child windows and layers.
        contents_view->RemoveChildView(host_view2);
        contents_view->AddChildViewAt(host_view2, 0);
        EXPECT_EQ("w2 w1", ChildWindowNamesAsString(*parent_window));
        EXPECT_EQ("w2 w1",
            ui::test::ChildLayerNamesAsString(*parent_window->layer()));

        // Work around for bug in NativeWidgetAura.
        // TODO: fix bug and remove this.
        parent->Close();
    }

    // It is possible to associate a window to a view which has a parent layer
    // (other than the widget layer). In this case, the parent layer of the host
    // view and the parent layer of the associated window are different. Test that
    // the layers and windows are properly reordered in this case.
    TEST_F(WindowReordererTest, HostViewParentHasLayer)
    {
        scoped_ptr<Widget> parent(CreateControlWidget(root_window(),
            gfx::Rect(0, 0, 100, 100)));
        parent->Show();
        aura::Window* parent_window = parent->GetNativeWindow();

        View* contents_view = new View();
        parent->SetContentsView(contents_view);

        // Create the following view hierarchy. (*) denotes views which paint to a
        // layer.
        //
        // contents_view
        // +-- v1
        //     +-- v11*
        //     +-- v12 (attached window)
        //     +-- v13*
        // +--v2*

        View* v1 = new View();
        contents_view->AddChildView(v1);

        View* v11 = new View();
        v11->SetPaintToLayer(true);
        v11->layer()->set_name("v11");
        v1->AddChildView(v11);

        scoped_ptr<Widget> w(CreateControlWidget(parent_window,
            gfx::Rect(0, 1, 100, 101)));
        SetWindowAndLayerName(w->GetNativeView(), "w");
        w->Show();

        View* v12 = new View();
        v1->AddChildView(v12);
        w->GetNativeView()->SetProperty(kHostViewKey, v12);

        View* v13 = new View();
        v13->SetPaintToLayer(true);
        v13->layer()->set_name("v13");
        v1->AddChildView(v13);

        View* v2 = new View();
        v2->SetPaintToLayer(true);
        v2->layer()->set_name("v2");
        contents_view->AddChildView(v2);

        // Test intial state.
        EXPECT_EQ("w", ChildWindowNamesAsString(*parent_window));
        EXPECT_EQ("v11 w v13 v2",
            ui::test::ChildLayerNamesAsString(*parent_window->layer()));

        // |w|'s layer should be stacked above |v1|'s layer.
        v1->SetPaintToLayer(true);
        v1->layer()->set_name("v1");
        EXPECT_EQ("w", ChildWindowNamesAsString(*parent_window));
        EXPECT_EQ("v1 w v2",
            ui::test::ChildLayerNamesAsString(*parent_window->layer()));

        // Test moving the host view from one view with a layer to another.
        v2->AddChildView(v12);
        EXPECT_EQ("w", ChildWindowNamesAsString(*parent_window));
        EXPECT_EQ("v1 v2 w",
            ui::test::ChildLayerNamesAsString(*parent_window->layer()));

        // Work around for bug in NativeWidgetAura.
        // TODO: fix bug and remove this.
        parent->Close();
    }

} // namespace
} // namespace views
